package top.huxy.boot.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.CacheControl;
import org.springframework.stereotype.Service;
import top.huxy.boot.BlogApiApplication;
import top.huxy.boot.config.Constant;
import top.huxy.boot.config.RedisKeyConfig;
import top.huxy.boot.service.MailService;
import top.huxy.boot.service.ImageService;
import top.huxy.boot.util.IPAddressUtils;
import top.huxy.boot.util.RequestUtils;
import top.huxy.boot.util.StringUtils;
import top.huxy.boot.util.obj.BinaryResult;
import top.huxy.boot.util.obj.Result;

import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @author HuXY
 * @Date 2021年12月30日14:28:35
 */
@Service
@Slf4j
public class ImageServiceImpl implements ImageService {

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    MailService mailService;

    @Override
    public boolean checkYzm(String yzm) {

        //请求的ip地址
        String requestIp = IPAddressUtils.getRequestIp();

        //验证码正确性判断
        Object o = redisTemplate.opsForValue().get(RedisKeyConfig.YZM_PREFIX + requestIp);
        if (StringUtils.isEmpty(o) || !o.toString().equalsIgnoreCase(yzm)) {
            if (BlogApiApplication.getEnv().isDev()) {
                log.info("验证码错误,right={},yzm={},ip={}", o, yzm, requestIp);
            }
            return true;
        }

        //验证码只能用一次，验证成功了就清除
        redisTemplate.delete(RedisKeyConfig.YZM_PREFIX + requestIp);
        return false;
    }

    @Override
    public void setYzm(String yzm) {
        redisTemplate.opsForValue().set(RedisKeyConfig.YZM_PREFIX + IPAddressUtils.getRequestIp(), yzm, 300L, TimeUnit.SECONDS);
    }

    @Override
    public BufferedImage createImageYzm() {
        StringBuilder sb = new StringBuilder(4);
        int height = 40;
        int width = 140;

        BufferedImage image = new BufferedImage(140, 40, BufferedImage.TYPE_INT_RGB);
        Graphics graphic = image.getGraphics();

        // 在 "画板"上生成干扰线条 ( 50 是线条个数)
        final int lineCount = 50;
        for (int i = 0; i < lineCount; i++) {
            graphic.setColor(getRandomColor(50, 120));
            final int x = ThreadLocalRandom.current().nextInt(width);
            final int y = ThreadLocalRandom.current().nextInt(height);
            final int w = ThreadLocalRandom.current().nextInt(20);
            final int h = ThreadLocalRandom.current().nextInt(20);
            final int signA = ThreadLocalRandom.current().nextBoolean() ? 1 : -1;
            final int signB = ThreadLocalRandom.current().nextBoolean() ? 1 : -1;
            graphic.drawLine(x + 5, y, x + w * signA, y + h * signB);
        }

        // 在 "画板"上绘制字母
        final int strCount = 4;
        final int fontSize = 30;
        final String str = "23456789ABCDEFGHJKMNPQRSTUVWXYZ";
        for (int i = 0; i < strCount; i++) {
            graphic.setFont(new Font(getRandomFont(), Font.BOLD, fontSize));
            String s = str.charAt(ThreadLocalRandom.current().nextInt(str.length())) + "";
            sb.append(s);
            graphic.setColor(getRandomColor(180, 255));
            graphic.drawString(s, i * ((int) (fontSize * 0.9)) + (i + 1) * 5 + 5, fontSize);
        }
        graphic.dispose();

        //设置验证码
        setYzm(sb.toString());

        return image;
    }

    private Color getRandomColor(int min, int max) {
        int r = ThreadLocalRandom.current().nextInt(max - min) + min;
        int g = ThreadLocalRandom.current().nextInt(max - min) + min;
        int b = ThreadLocalRandom.current().nextInt(max - min) + min;
        return new Color(r, g, b);
    }

    static String[] fontArray;

    private String getRandomFont() {
        if (fontArray == null) {
            fontArray = new String[]{"宋体", "楷体", "黑体"};
        }
        return fontArray[ThreadLocalRandom.current().nextInt(fontArray.length)];
    }

    @Override
    public BinaryResult imgProxy(String url) {
        Object data = redisTemplate.opsForValue().get(Constant.IMAGE_PROXY + url);
        if (data instanceof byte[]) {
            return BinaryResult.send((byte[]) data, Result.success("缓存"));
        }

        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(url);
            try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
                int code = response.getCode();
                HttpServletResponse currentResponse = RequestUtils.getCurrentResponse();
                if (code != 200) {
                    currentResponse.setStatus(404);
                    return BinaryResult.send(null, Result.notFound("404"));
                }
                HttpEntity entity = response.getEntity();
                try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                    entity.writeTo(byteArrayOutputStream);
                    //缓存策略

                    String contentType = entity.getContentType();
                    currentResponse.setContentType(contentType);
                    CacheControl cacheControl = CacheControl.maxAge(365, TimeUnit.DAYS);
                    currentResponse.setHeader("Cache-Control", cacheControl.getHeaderValue());
                    byte[] bytes = byteArrayOutputStream.toByteArray();
                    redisTemplate.opsForValue().set(Constant.IMAGE_PROXY + url, bytes, 1L, TimeUnit.DAYS);
                    return BinaryResult.send(bytes, Result.success("完成"));
                }
            }
        } catch (IOException e) {
            return BinaryResult.send(null, Result.serverError("err"));
        }
    }
}
